package com.lcb.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/****
 * @Author:刘长柏
 * @Date: 2021-02-19 10:48
 * @Description:NIO服务器端代码
 *****/
public class NIOServer {
    public static void main(String[] args) throws IOException {
        //创建一个在本地端口进行监听的服务socket通道,并设置非阻塞方式
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //必须配置非阻塞才能往selector上注册,否则会报错,selector模式本身就是非阻塞模式
        ssc.configureBlocking(false);
        ssc.socket().bind(new InetSocketAddress(9000));
        //创建一个选择器selector
        Selector selector = Selector.open();
        //把ServerSocketChannel注册到Selector上,并且Selector对客户端的appept连接感兴趣
        ssc.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            System.out.println("等待事件发生...");
            //轮询监听channel里的key,select是阻塞的,accept也是阻塞的
            int select = selector.select();
            System.out.println("有事件发生了...");
            //有客户端请求,被轮询监听到
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                //删除本次已经处理的key,防止下次select重复处理
                iterator.remove();
                handler(key);
            }
        }
    }

    private static void handler(SelectionKey key) throws IOException {
        if (key.isAcceptable()) {
            System.out.println("有客户端连接事件发生了...");
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            //NIO非阻塞体现:此处accept方法是阻塞的,但是这里因为发生连接事件,所有这个方法马上执行完,不会阻塞
            //处理完连接请求不会继续等待客户端的连接
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);
            //通过Selector监听Channel时对读事件感兴趣
            sc.register(key.selector(), SelectionKey.OP_READ);
        } else if (key.isReadable()) {
            System.out.println("有读事件发生了...");
            SocketChannel sc = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //NIO 非阻塞体现: 首先read方法不会阻塞,其次这种事件响应模型,当调用到read方法时肯定是发生了客户端发送数据事件
            int len = sc.read(buffer);
            if (len != -1) {
                System.out.println("读取到客户端发送到的数据: " + new String(buffer.array(), 0, len));
            }
            ByteBuffer bufferToWrite = ByteBuffer.wrap("HelloClient".getBytes());
            sc.write(bufferToWrite);
            key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        } else if (key.isWritable()) {
            SocketChannel sc = (SocketChannel) key.channel();
            System.out.println("write事件发生了...");
            //NIO事件触发是水平触发
            //使用java的NIO编程时,在没有往外写的时候要取消写时间
            //在有往外写的时候在注册写时间
            key.interestOps(SelectionKey.OP_WRITE);
        }
    }
}
